#include "flag.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define CCLIERROR(a) {printf("%s\n", a); exit(-1);}
#define CCLIERRORFLAG(a) {printf("%s flag not exist\n", a); exit(-1);}

//CCLIFlagHandlerInit: init
CCLIFlagHandler *CCLIFlagHandlerInit(){
    CCLIFlagHandler *handler = malloc(sizeof(*handler));
    memset(handler, 0, sizeof(*handler));
    return handler;
}

static int CCLIFlagSet(
    CCLIFlagHandler *handler,
    void *var,
    int varSize,
    char *shortName,
    char *longName,
    char *description,
    CCLIFlagType type)
{
    if(!handler || !var || !shortName || !longName || !description)
        CCLIERROR("invalid arg");
    CCLIFlagList *flag = malloc(sizeof(*flag));
    flag->entry.data = var;
    flag->entry.shortName = strdup(shortName);
    flag->entry.longName = strdup(longName);
    flag->entry.description = strdup(description);
    flag->entry.type = type;
    flag->entry.dataSize = varSize;

    if(handler->head){
        handler->tail->next = flag;
        handler->tail = flag;
    }else{
        handler->head = flag;
        handler->tail = flag;
    }
    return 0;
}

int CCLIFlagSetInt(CCLIFlagHandler *handler, int *var, char *shortName, char *longName, int defaultVar, char *description){
    *var = defaultVar;
    return CCLIFlagSet(handler, var, 0, shortName, longName, description, CCLIFlagTypeInt);
}

int CCLIFlagSetBool(CCLIFlagHandler *handler, int *var, char *shortName, char *longName, int defaultVar, char *description){
    *var = defaultVar;
    return CCLIFlagSet(handler, var, 0, shortName, longName, description, CCLIFlagTypeBool);
}

static void CCLISetFlagValue(CcliFlag *entry, char *val){
    switch(entry->type){
        case CCLIFlagTypeInt:
        case CCLIFlagTypeBool:
        {
            int *ptr = entry->data;
            *ptr = atoi(val);
            break;
        }
        default:
            CCLIERROR("Unkown value type, it's bug");
    }
}

//try to find flag entry by long name
static CcliFlag * CCLIFlagParseLongName(CCLIFlagList *list, char *argv, int *ifCacheVal){
    CCLIFlagList *ptr = list;
    char *pos = strchr(argv, '=');
    if(pos){
        *ifCacheVal = 1;
        *pos = '\0';
        pos++;
    }
    while(ptr){
        if(strcmp(ptr->entry.longName, argv) == 0){
            if(ifCacheVal){
                CCLISetFlagValue(&ptr->entry, pos);
            }
            return &ptr->entry;
        }
        ptr = ptr->next;
    }

    CCLIERRORFLAG("argv");
    return NULL;
}

static CcliFlag * CCLIFlagParseShortName(CCLIFlagList *list, char *argv){
    CCLIFlagList *ptr = list;

    while(ptr){
        if(strcmp(ptr->entry.shortName, argv) == 0){
            return &ptr->entry;
        }
        ptr = ptr->next;
    }

    CCLIERRORFLAG("argv");
    return NULL;
}


//CCLIFlagParse: parse flag and return the not parsed argv
//stop parse when meet unset flag, or meet the word is not flag
int CCLIFlagParse(CCLIFlagHandler *handler, int *argc, char ***argv){
    if(*argc == 0)
        return 0;

    //现阶段都是线性查找
    CcliFlag * curFlag = NULL;
    int ifCatchVal = 1;
    char **ptr = *argv;
    while(ptr){
        char *word = *ptr;
        if(!word || (*word != '-' && ifCatchVal)){
            break;
        }
        if(*word == '-'){
            ifCatchVal = 0;
            if(*(word + 1) == '-'){
                curFlag = CCLIFlagParseLongName(handler->head, word + 2, &ifCatchVal);
            }else{
                curFlag = CCLIFlagParseShortName(handler->head, word + 1);
            }
        }else{
            ifCatchVal = 1;
            CCLISetFlagValue(curFlag, word);
        }
        *argc--;
        *argv++;
        ptr++;
    }
    return 0;
}